package leetcode.design;

import java.util.*;

class Twitter {

    Map<Integer, User> userMap = new HashMap<>();
    private int globalTime;
    private final int tweetCount = 10;

    public Twitter() {
        this.globalTime = 0;
    }

    static class User {
        public int userId;
        public LinkedList<User> followeeUsers = new LinkedList<>();
        public LinkedList<Tweet> postTweets = new LinkedList<>();

        public User(int userId) {
            this.userId = userId;
        }
    }

    static class Tweet {
        public int tweetId;
        public int userId;
        public int tweetTime;

        public Tweet(int tweetId, int userId, int tweetTime) {
            this.tweetId = tweetId;
            this.userId = userId;
            this.tweetTime = tweetTime;
        }
    }

    public void postTweet(int userId, int tweetId) {
        User user;
        if (userMap.containsKey(userId)) {
            user = userMap.get(userId);
        } else {
            user = registerUser(userId);
        }
        LinkedList<Tweet> tweets = user.postTweets;
        if (tweets.size() == tweetCount) {
            tweets.removeLast();
        }
        globalTime++;
        tweets.addFirst(new Tweet(tweetId, userId, globalTime));
    }

    public List<Integer> getNewsFeed(int userId) {
        User user = userMap.get(userId);
        if (user == null) {
            return Collections.emptyList();
        }
        PriorityQueue<Tweet> queue = new PriorityQueue<>((o1, o2) -> o2.tweetTime - o1.tweetTime);
        List<User> userList = new ArrayList<>();
        userList.add(user);
        for (User followee : user.followeeUsers) {
            if (followee.userId != userId) {
                userList.add(followee);
            }
        }
        Map<User, Integer> next = new HashMap<>();
        for (User curUser : userList) {
            next.put(curUser, 0);
        }
        List<Integer> result = new ArrayList<>();
        for (User curUser : userList) {
            List<Tweet> postTweets = curUser.postTweets;
            Integer index = next.get(curUser);
            if (index < postTweets.size()) {
                queue.add(postTweets.get(index));
            }
            next.merge(curUser, 1, Integer::sum);
        }
        for (int i = 0; i < tweetCount; i++) {
            if (queue.isEmpty()) {
                break;
            }
            Tweet tweet = queue.poll();
            result.add(Objects.requireNonNull(tweet).tweetId);
            User curUser = userMap.get(tweet.userId);
            List<Tweet> postTweets = curUser.postTweets;
            Integer index = next.get(curUser);
            if (index < postTweets.size()) {
                queue.add(postTweets.get(index));
                next.merge(curUser, 1, Integer::sum);
            }
        }
        return result;
    }

    public void follow(int followerId, int followeeId) {
        User followee = userMap.get(followeeId);
        if (followee == null) {
            followee = registerUser(followeeId);
        }
        User follower = userMap.get(followerId);
        if (follower == null) {
            follower = registerUser(followerId);
        }
        follower.followeeUsers.add(followee);
    }

    public void unfollow(int followerId, int followeeId) {
        userMap.get(followerId).followeeUsers.remove(userMap.get(followeeId));
    }

    private User registerUser(int userId) {
        User user = new User(userId);
        userMap.put(userId, user);
        return user;
    }
}

/**
 * Your Twitter object will be instantiated and called as such:
 * Twitter obj = new Twitter();
 * obj.postTweet(userId,tweetId);
 * List<Integer> param_2 = obj.getNewsFeed(userId);
 * obj.follow(followerId,followeeId);
 * obj.unfollow(followerId,followeeId);
 */

public class leetcode355 {
    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
        test4();
    }

    private static void test4() {
        // ["Twitter","postTweet","postTweet","postTweet","postTweet","postTweet",
        // [[],[2,5],[1,3],[1,101],[2,13],[2,10],
        // "postTweet","postTweet","postTweet","postTweet","postTweet","getNewsFeed","follow","getNewsFeed"]
        // [1,2],[2,94],[2,505],[1,333],[1,22],[2],[2,1],[2]]
        Twitter twitter = new Twitter();
        twitter.postTweet(2, 5);
        twitter.postTweet(1, 3);
        twitter.postTweet(1, 101);
        twitter.postTweet(2, 13);
        twitter.postTweet(2, 10);
        twitter.postTweet(1, 2);
        twitter.postTweet(2, 94);
        twitter.postTweet(2, 505);
        twitter.postTweet(1, 333);
        twitter.postTweet(1, 22);
        System.out.println(twitter.getNewsFeed(2));
        twitter.follow(2, 1);
        System.out.println(twitter.getNewsFeed(2));
    }

    private static void test3() {
        // ["Twitter","postTweet","follow","follow","getNewsFeed","postTweet","getNewsFeed","getNewsFeed","unfollow","getNewsFeed","getNewsFeed","unfollow","getNewsFeed","getNewsFeed"]
        // [[],[1,5],[1,2],[2,1],[2],[2,6],[1],[2],[2,1],[1],[2],[1,2],[1],[2]]
        // [null,null,null,null,[5],null,[6,5],[6,5],null,[6,5],[6],null,[5],[6]]
        Twitter twitter = new Twitter();
        twitter.postTweet(1, 5);
        twitter.follow(1, 2);
        twitter.follow(2, 1);
        System.out.println(twitter.getNewsFeed(2)); // [5]
        twitter.postTweet(2, 6);
        System.out.println(twitter.getNewsFeed(1)); // [6,5]
        System.out.println(twitter.getNewsFeed(2)); // [6,5]
        twitter.unfollow(2, 1);
        System.out.println(twitter.getNewsFeed(1)); // [6,5]
        System.out.println(twitter.getNewsFeed(2)); // [6]
        twitter.unfollow(1, 2);
        System.out.println(twitter.getNewsFeed(1)); // [5]
        System.out.println(twitter.getNewsFeed(2)); // [6]
    }

    private static void test2() {
        Twitter twitter = new Twitter();
        twitter.postTweet(1, 5);
        twitter.postTweet(1, 3);
        System.out.println(twitter.getNewsFeed(1)); // [3,5]
    }

    private static void test1() {
        Twitter twitter = new Twitter();
        twitter.postTweet(1, 5); // 用户 1 发送了一条新推文 (用户 id = 1, 推文 id = 5)
        System.out.println(twitter.getNewsFeed(1));  // 用户 1 的获取推文应当返回一个列表，其中包含一个 id 为 5 的推文
        twitter.follow(1, 2);    // 用户 1 关注了用户 2
        twitter.postTweet(2, 6); // 用户 2 发送了一个新推文 (推文 id = 6)
        System.out.println(twitter.getNewsFeed(1));  // 用户 1 的获取推文应当返回一个列表，其中包含两个推文，id 分别为 -> [6, 5] 。推文 id 6 应当在推文 id 5 之前，因为它是在 5 之后发送的
        twitter.unfollow(1, 2);  // 用户 1 取消关注了用户 2
        System.out.println(twitter.getNewsFeed(1));  // 用户 1 获取推文应当返回一个列表，其中包含一个 id 为 5 的推文。因为用户 1 已经不再关注用户 2
    }
}
